package com.flca.frw.util;

import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.StringTokenizer;

import javax.xml.crypto.dsig.TransformException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.lang.StringUtils;


/**
 * Dit is een helper class waarmee veel voorkomende transformaties van het ene. 
 * naar het andere datatype  * kunnen worden uitgevoerd. 
 * Alle transformaties zijn er in twee x twee smaken, eentje waarbij 
 * een Exception gegooid kan worden en eentje waarbij er een Signalering wordt 
 * toegevoegd aan SignalOptions, die in dat geval als parameter is meegegeven.
 * En daarnaast is ook nog de mogelijkheid om een default waarde mee te 
 * geven. Deze wordt gebruikt als de inputvalue null (of leeg is). De
 * default waarde kan ook weer een null waarde zijn. Als een default waarde
 * wordt meegegeven, dan zal er natuurlijk exception worden gegooid als 
 * input leeg was, maar wel als inputwaarde niet geconverteerd kan worden.
 * Nb sommige transformaties, zoals int2String() zijn heel flauw, maar zijn er om
 * generatoren indentieke code te laten genereren.
 * <br/>
 * Voorbeeld.
 * TransformUtils tu = new TransformUtils(); 
 * Date eenDatum = tu.string2Date(eenString);
 * Long long = tu.string2Long(eenAndereString);
 * tu.getSignals(); 
 * <br/>
 * 
 * @author rbakkerus
 * @version $Date: $ $Revision: $
 * 
 */
public final class TransformUtils implements Serializable
{
	private static final long serialVersionUID = -9105993721163261667L;

	private static TransformUtils sInstance;
	private static SimpleDateFormat sDefaultDateFmt = new SimpleDateFormat("dd-MM-yyyy");
	private static SimpleDateFormat sDefaultTimeFmt = new SimpleDateFormat("hh:mm");

	private TransformUtils() {
	}

	public static TransformUtils getInstance() {
		if (sInstance == null) {
			sInstance = new TransformUtils();
		}
		return sInstance;
	}
	/**
	 * Transformeert een string representatie van een datum naar een Datum.
	 * @param aString
	 * @return de Datum volgens de string.
	 * @throws Exception 
	 */
	//----- verschillende string2Date utils ----

	public Date string2Date(String aString) throws TransformException {
		return string2Date(aString, sDefaultDateFmt);
	}

	/**
	 * Transformeert een string representatie van een datum naar een Datum.
	 * @param aString
	 * @param aDateFormat
	 * @param aOptions
	 * @return Date
	 */
	public Date string2Date(String aValue, SimpleDateFormat aDateFormat) throws TransformException {

		if (isNotNull(aValue)) {
			try {
				// get rid of the time 
				StringTokenizer tokens = new StringTokenizer(aValue.trim() + " ", " ");
				String datestr = tokens.nextToken();

				Date retDate = aDateFormat.parse(datestr);
				return retDate;
			} catch (Exception e) {
				handleError("string2Date", e, aValue);
				return null;	
			}
		} else {
			return null;
		}
	}

	/**
	 * @param aString
	 * @param aDefaultDatum
	 * @return Date
	 * @throws Exception
	 */
	public Date string2Date(String aString, Date aDefaultDatum) throws TransformException {
		return string2Date(aString, sDefaultDateFmt, aDefaultDatum);
	}

	/**
	 * @param aString
	 * @param aDateFormat
	 * @param aDefaultDatum
	 * @return Date
	 * @throws TransformException
	 */
	public Date string2Date(String aString, SimpleDateFormat aDateFormat, Date aDefaultDatum) throws TransformException {
		if (StringUtils.isEmpty(aString)) {
			return aDefaultDatum;
		} else {
			return string2Date(aString, aDateFormat);
		}
	}

	//----- verschillende strToint utils ----

	/**
	 * Method string2int.
	 * @param aString
	 * @param aMsg
	 * @return int
	 */
	public int string2int(String aValue) throws TransformException {

		if (isNotNull(aValue)) {
			try {
				return Integer.parseInt(removeZeros(aValue));
			} catch (Exception ex) {
				//String msg = "invalid int " + " (" + aValue + ")";
				handleError("string2int", ex, aValue);
				return 0;
			}
		} else {
			return 0;
		}
	}

	/**
	 * @param aString
	 * @param aDefault
	 * @param aOptions
	 * @return int
	 */
	public int string2int(String aString, int aDefault) throws TransformException {

		if (StringUtils.isEmpty(aString)) {
			return aDefault;
		} else {
			return string2int(aString);
		}
	}

	/**
	 * Method string2Integer.
	 * @param aString
	 * @return Integer
	 * @throws TransformException
	 */
	public Integer string2Integer(String aString) throws TransformException {
		if (isNotNull(aString)) {
			try {
				return new Integer(aString);
			} catch (NumberFormatException e) {
				handleError("string2Integer", e, aString);
				return null;
			}
		} else {
			return new Integer(0);
		}
	}

	/**
	 * Method string2Integer.
	 * @param aString
	 * @return Integer
	 * @throws TransformException
	 */
	public Long string2Long(String aString) throws TransformException {
		return new Long(string2long(aString));
	}

	public long string2long(String aString) throws TransformException {
		if (isNotNull(aString)) {
			try {
				return Long.parseLong(aString);
			} catch (NumberFormatException e) {
				handleError("string2Integer", e, aString);
				return 0;
			}
		} else {
			return 0;
		}
	}

	/**
	 * Method string2Integer.
	 * @param aString
	 * @return Integer
	 * @throws TransformException
	 */
	public Long string2Long(String aString, Long defValue) throws TransformException {
		if (StringUtils.isEmpty(aString)) {
			return defValue;
		} else {
			return string2Long(aString);
		}
	}
	
	/**
	 * Method string2short.
	 * @param aString
	 * @return short
	 * @throws TransformException
	 */
	public short string2short(String aString) throws TransformException {
		if (isNotNull(aString)) {
			try {
				return Short.parseShort(aString);
			} catch (NumberFormatException e) {
				handleError("string2Integer", e, aString);
				return (short) 0;
			}
		} else {
			return (short) 0;
		}
	}

	/**
	 * Method string2Short.
	 * @param aString
	 * @return Short
	 * @throws TransformException
	 */
	public Short string2Short(String aString) throws TransformException {
		return new Short(string2short(aString));
	}

	//----- verschillende float2Str utils ----

	/**
	 * Method int2String.
	 * @param aValue
	 * @return String
	 */
	public String float2String(double aValue) {
		return "" + aValue;
	}

	/**
	 * @param aValue
	 * @param aOptions
	 * @return String
	 * @throws TransformException
	 */
	public String float2String(Double aValue) throws TransformException {
		isNotNull(aValue);
		return float2String(aValue.doubleValue());
	}

	//----- verschillende string2float utils ----

	/**
	 * Method string2float.
	 * @param aValue
	 * @param aOptions
	 * @return double
	 */
	public double string2double(String aValue) throws TransformException {

		isNotNull(aValue);
		if (aValue != null) {
			try {
				double d = Double.parseDouble(aValue);
				return d;
			} catch (NumberFormatException ex) {
				String msg = "invalid float " + " (" + aValue + ")";
				handleError("string2Integer " + msg, ex, aValue);
				return 0.0;
			}
		} else {
			return 0.0;
		}
	}

	/**
	 * Method string2double.
	 * @param aString
	 * @param aDefault
	 * @param aOptions
	 * @return double
	 */
	public double string2double(String aString, double aDefault) throws TransformException {

		if (StringUtils.isEmpty(aString)) {
			return aDefault;
		} else {
			return string2double(aString);
		}
	}

	/**
	 * Method string2Double.
	 * @param aString
	 * @return Double
	 * @throws TransformException
	 */
	public Double string2Double(String aString) throws TransformException {
		return new Double(string2double(aString));
	}

	/**
	 * Method string2float.
	 * @param aValue
	 * @return float
	 * @throws TransformException
	 */
	public float string2float(String aValue) throws TransformException {
		return (float) string2double(aValue);
	}

	/**
	 * Method string2Float.
	 * @param aValue
	 * @return Float
	 * @throws TransformException
	 */
	public Float string2Float(String aValue) throws TransformException {
		return new Float((float) string2double(aValue));
	}

	public double bigDecimal2double(BigDecimal aValue) {
		if (aValue != null) {
			return aValue.doubleValue();
		} else {
			return 0;
		}
	}

	public Integer bigDecimal2Integer(BigDecimal aValue) {
		if (aValue != null) {
			return new Integer(aValue.intValue());
		} else {
			return null;
		}
	}

	//TODO extra parameter indien we niet willen dat formaat wordt aangepast? (of in ctor)
	//TODO formatering verbeteren
	public String bigDecimal2String(BigDecimal aValue) {
		if (aValue != null) {
			String s = aValue.toPlainString();
			int dotAt = s.indexOf(".");
			if (dotAt > 0 && s.length() > dotAt + 4) {
				s = s.substring(0, dotAt + 3);
				if (s.endsWith("99")) {
					s = s.substring(0, s.length() -2) + "00";
				}
			}
			return s;
		} else {
			return null;
		}
	}

	public Double bigDecimal2Double(BigDecimal aValue) {
		return new Double(bigDecimal2double(aValue));
	}

	public BigDecimal double2BigDecimal(double aValue) {
		return string2BigDecimal("" + aValue);
	}

	public BigDecimal double2BigDecimal(Double aValue) {
		if (aValue != null) {
			return double2BigDecimal(aValue.doubleValue());
		} else {
			return null;
		}
	}
	
	public BigDecimal string2BigDecimal(String aValue) {
		if (aValue != null && aValue.trim().length() > 0) {
			return new BigDecimal(aValue);
		} else {
			return null;
		}
	}
	
	public BigDecimal integer2BigDecimal(Integer aValue) {
		if (aValue != null) {
			return new BigDecimal(aValue.intValue());
		} else {
			return null;
		}
	}

	//----- verschillende string2Bool utils ----
	
	public boolean string2boolean(String aValue)  {
		if (aValue != null) {
			String s = aValue.toUpperCase().substring(0, 1);
			return (s.equals("T") || s.equals("1") || s.equals("J") || s.equals("Y"));
		} else {
			return false;
		}
	}
	
	public Boolean string2Boolean(String aValue)  {
		if (aValue != null) {
			return new Boolean(string2boolean(aValue));
		} else {
			return null;
		}
	}

	public String boolean2String(Boolean aValue) {
		if (aValue != null) {
			return boolean2String(aValue.booleanValue());
		} else {
			return null;
		}
	}

	public String boolean2String(boolean aValue) {
		if (aValue) {
			return "T";
		} else {
			return "F";
		}
	}
	
	/**
	 * Method sqlDate2UtilDate.
	 * @param aValue
	 * @return Date
	 */
	//----- verschillende date2UtilDate utils ----

	public java.util.Date sqlDate2UtilDate(java.sql.Date aValue) {
		if (aValue != null) {
			java.util.Date date = new java.util.Date(aValue.getTime());
			return date;
		} else {
			return null;
		}
	}
	/**
	 * Method sqlDate2UtilDate.
	 * @param aValue
	 * @return Date
	 */
	//----- verschillende date2UtilDate utils ----

	public java.util.Date utilDate2UtilDate(java.util.Date aDateValue) {
		if (aDateValue != null) {
			return new java.util.Date(aDateValue.getTime());
		} else {
			return null;
		}
	}
	/**
	 * Method sqlDate2UtilDate.
	 * @param aValue
	 * @return Date
	 */
	//----- verschillende date2UtilDate utils ----

	public java.util.Date sqlDate2UtilDate(java.util.Date aValue) {
		if (aValue != null) {
			return new java.util.Date(aValue.getTime());
		} else {
			return null;
		}
	}


	//----- verschillende date2SqlDate utils ----

	/**
	 * Method date2SqlDate.
	 * @param aDate
	 * @param aOptions
	 * @return Date
	 */
	public java.sql.Date date2SqlDate(java.util.Date aValue) {
		if (aValue != null) {
			return new java.sql.Date(aValue.getTime());
		} else {
			return null;
		}
	}

	//----- verschillende date2SqlTime utils ----

	/**
	 * Method date2SqlTime.
	 * @param aDate
	 * @param aOptions
	 * @return Time
	 */
	public java.sql.Time date2SqlTime(java.util.Date aValue) {
		if (aValue != null) {
			return new java.sql.Time(aValue.getTime());
		} else {
			return null;
		}
	}

	//----- verschillende date2SqlTimeStamp utils ----

	/**
	 * Method date2SqlDate.
	 * @param aDate
	 * @param aOptions
	 * @return Timestamp
	 */
	public Timestamp date2SqlTimeStamp(java.util.Date aValue) {
		if (aValue != null) {
			return new Timestamp(aValue.getTime());
		} else {
			return null;
		}
	}

	//----- verschillende date2Str utils ----

	/**
	 * Method date2Str.
	 * @param aDate
	 * @return String
	 */
	public String date2String(java.util.Date aDate) {
		return date2String(aDate, getDefaultDateFmt());
	}

	/**
	 * Method date2Str.
	 * @param aDate
	 * @param aDefault
	 * @return String
	 */
	public String date2String(java.util.Date aDate, String aDefault) {
		if (aDate == null) {
			return aDefault;
		} else {
			return date2String(aDate, getDefaultDateFmt());
		}
	}

	/**
	 * Method date2Str.
	 * @param aDate
	 * @param aDateFmt
	 * @return String
	 */
	public String date2String(java.util.Date aDate, SimpleDateFormat aDateFormat) {
		if (aDate != null) {
			return aDateFormat.format(aDate);
		} else {
			return null;
		}
	}

	/**
	 * Method date2Str.
	 * @param aDate
	 * @param aDateFormat
	 * @param aDefault
	 * @return String
	 */
	public String date2String(
		java.util.Date aDate,
		SimpleDateFormat aDateFormat,
		String aDefault) {

		if (aDate == null) {
			return aDefault;
		} else {
			return date2String(aDate, aDateFormat);
		}
	}
		
	//--- verschillende int2Str methodes
	/**
	 * Method int2Str.
	 * beetje flauwe methode, is alleen maat toegeveogd om het code generators makkelijk te maken
	 * @param aValue
	 * @return String
	 */
	public String int2String(int aValue) {
		return "" + aValue;
	}

	/**
	 * Method int2Str.
	 * @param aValue
	 * @return String
	 */
	public String long2String(long aValue) {
		return "" + aValue;
	}

	/**
	 * Method int2Str.
	 * beetje flauwe methode, is alleen maat toegeveogd om het code generators makkelijk te maken
	 * @param aValue
	 * @return String
	 */
	public String short2String(short aValue) {
		return "" + aValue;
	}

	/**
	 * Method int2Str.
	 * beetje flauwe methode, is alleen maat toegeveogd om het code generators makkelijk te maken
	 * @param aValue
	 * @return String
	 */
	public String short2String(Short aValue) {
		return "" + aValue;
	}

	/**
	 * Method int2Str.
	 * @param aValue
	 * @param aOptions
	 * @return String
	 */
	public String integer2String(Integer aValue) {
		if (isNotNull(aValue)) {
			return "" + aValue.intValue();
		} else {
			return null;
		}
	}


	/**
	 * Method int2Str.
	 * @param aValue
	 * @param aOptions
	 * @return String
	 */
	public String long2String(Long aValue) {
		if (isNotNull(aValue)) {
			return "" + aValue.longValue();
		} else {
			return null;
		}
	}

	/**
	 * @param aValue
	 * @param aDefault
	 * @return String
	 */
	public String integer2String(Integer aValue, String aDefault) {
		if (aValue == null) {
			return aDefault;
		} else {
			return "" + aValue.intValue();
		}
	}

	/**
	 * Method int2Str.
	 * @param aValue
	 * @param aDefault
	 * @return String
	 */
	public String long2String(Long aValue, String aDefault) {
		if (aValue == null) {
			return aDefault;
		} else {
			return "" + aValue.longValue();
		}
	}

	/**
	 * Method string2Str.
	 * Deze voert een trim() uit aValue niet null is.
	 * @param aValue
	 * @return String
	 */
	public String string2String(final String aValue) {
		if (aValue == null) {
			return null;
		} else {
			return aValue.trim();
		}
	}

	// ---- misc ----

	/**
	 * Deze verwijderd alle spaties en nullen aan het begin van een String
	 * Method removeZeros.
	 * @param aValue
	 * @return String
	 */
	public String removeZeros(String aValue) {
		if (aValue != null) {
			String retstr = aValue.trim();
			while (retstr.startsWith("0") && retstr.length() > 1) {
				retstr = retstr.substring(1, retstr.length());
			}

			return retstr;
		} else {
			return null;
		}
	}

	/**
	 * Method getDefaultDateFmt.
	 * Deze geeft het formaat dd-MM-yyyy terug
	 * @return SimpleDateFormat 
	 */
	public SimpleDateFormat getDefaultDateFmt() {
		return sDefaultDateFmt;
	}

	/**
	 * Method checkNullValue.
	 * @param aValue
	 * @param aOptions
	 */
	private boolean isNotNull(Object aValue) {
		if (aValue == null) {
			return false;
		} else if (aValue instanceof String) {
			if (((String) aValue).trim().length() == 0) {
				return false;
			}
		}
		return true;
	}

	//----------- several object2Xxx methods
	
	/**
	 * Method Object2Str.
	 * @param aObject
	 * @return String
	 */
	public String object2String(Object aValue) throws TransformException {
		if (isNotNull(aValue) == false) {
			return null;
		} else if (aValue instanceof java.util.Date) {
			return date2String((java.util.Date) aValue);
		} else if (aValue instanceof BigDecimal) {
			return bigDecimal2String((BigDecimal) aValue);
		} else {
			return aValue.toString();
		}
	}

	public Integer object2Integer(Object aValue) {
		if (aValue != null) {
			if (aValue instanceof Integer) {
				return (Integer) aValue;
			} else {
				return new Integer(aValue.toString());
			}
		} else {
			return null;
		}
	}

	public Long object2Long(Object aValue) {
		if (aValue != null) {
			if (aValue instanceof Long) {
				return (Long) aValue;
			} else {
				return new Long(aValue.toString());
			}
		} else {
			return null;
		}
	}

	public BigDecimal object2BigDecimal(Object aValue) {
		if (aValue != null) {
			if (aValue instanceof BigDecimal) {
				return (BigDecimal) aValue;
			} else {
				return new BigDecimal(aValue.toString());
			}
		} else {
			return null;
		}
	}

	public java.util.Date object2Date(Object aValue) throws TransformException {
		if (aValue != null) {
			if (aValue instanceof java.util.Date) {
				return (java.util.Date) aValue;
			} else {
				return string2Date(aValue.toString());
			}
		} else {
			return null;
		}
	}

	public Boolean object2Bool(Object aValue) throws TransformException {
		if (aValue != null) {
			if (aValue instanceof Boolean) {
				return (Boolean) aValue;
			} else {
				return string2Boolean(aValue.toString());
			}
		} else {
			return null;
		}
	}

	/**
	 * Method timeStampToDate.
	 * @param timestamp
	 * @return Date
	 */
	public java.util.Date timestamp2Date(Timestamp timestamp) { 
		return new java.util.Date(timestamp.getTime());
	}

	/**
	 * Method time2Str.
	 * Deze haalt uit een Date, het tijdgedeelte en formateerd deze naar een string volgens 
	 * standaard formaat: hh:mm 
	 * @param aDate
	 * @return String
	 */
	public String time2String(java.util.Date aDate) {
		return time2String(aDate, sDefaultTimeFmt);
	}

	/**
	 * Method time2Str.
	 * Deze haalt uit een Date, het tijdgedeelte en formateerd deze naar een string volgens 
	 * standaard formaat: hh:mm 
	 * @param aDate
	 * @param aDefault
	 * @return String
	 */
	public String time2String(java.util.Date aDate, String aDefault) {
		if (aDate == null) {
			return aDefault;
		} else {
			return time2String(aDate, sDefaultTimeFmt);
		}
	}

	/**
	 * Method time2Str.
	 * Deze haalt uit een Date, het tijdgedeelte en formateerd deze naar een string volgens 
	 * het formaat dat wordt opgegeven. 
	 * @param aDate
	 * @param aDateFmt
	 * @return String
	 */
	public String time2String(java.util.Date aDate, SimpleDateFormat aDateFormat) {
		if (aDate != null) {
			return aDateFormat.format(aDate);
		} else {
			return null;
		}
	}
	
	private void handleError(String msg, Exception ex, Object val) throws TransformException 
	{
		String p2 = "";
		if (val != null) {
			p2 = val.toString();
		}
		throw new TransformException(msg + ex.toString() + " " + p2);
	}
	
	public XMLGregorianCalendar dateToXMLGregorianCalendar(Date aValue) throws DatatypeConfigurationException {
		if (aValue == null) {
			return null;
		} else {
			GregorianCalendar c = new GregorianCalendar();
			c.setTime(aValue);
			return DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
		}
	}

	public Date xMLGregorianCalendarToDate(XMLGregorianCalendar aValue)  throws TransformException {
		if (aValue == null) {
			return null;
		} else {
			return new Date(aValue.toGregorianCalendar().getTimeInMillis());
		}
	}
}
